// 当 function 里嵌套 function 时，内部的 function 可以访问外部 function 里的变量。
function foo (x) {
    var tmp = 3;

    function bar (y) {
        console.log('bar: ', x + y + (++tmp)); // bar: 16
    }
    bar (10);
}
foo(2)
// 不管执行多少次，都会输出 16，因为 bar 能访问 foo 的参数 x，也能访问 foo 的变量 tmp。
// 但，这还不是闭包。
// 当 return 的是内部 function 时，就是一个闭包。
// 外部 function 的变量会保存直到内部 function 的结束。

function foo (x) {
    var tmp = 3;
    return function (y) {
        console.log(x + y + (++tmp));
    }
}
var bar = foo(2) // bar 现在就是一个闭包 
bar(10) // 16
bar(10) // 17
bar(10) // 18
// 虽然 bar 不直接处于 foo 的内部作用域，但 bar 还是能访问 x 和 tmp。
// 每次调用 bar 时都会自加1。

// 上面的 x 是一个字面值（值传递），当调用 foo 时，实参 x 的值被复制了一份，复制的一份作为了 foo 的参数 x。
// 问题来了，JS 里 object 是引用传递的，当你调用 foo 时传递一个 object，foo 函数 return 的闭包也会引用最初的 object。
function foo (x) {
    var tmp = 3;
    return function (y) {
        console.log(x + y + tmp);
        x.memb = x.memb ? x.memb + 1 : 1
        console.log('x.memb', x.memb)
    }
}
var age = new Number(2)
var bar = foo(age) // bar 是引用了 age 的闭包
bar(10) // 15 x.memb 1
bar(10) // 15 x.memb 2

// 每次运行 bar(10)，x.memb 都会自加1。
// x 每次都是指向同一个 object 变量——age，运行两次 bar(10) 后，age.memb = 2


// 这是不用 return 的闭包
function closureExample (objID, text, timedelay) {
    setTimeout(function () {
        document.getElementById(objID).innerHTML = text
    }, timedelay);
}

closureExample('myDiv', 'Closure is created', 500)

/**
 * JS里的function能访问它们的：
    1. 参数
    2. 局部变量或函数
    3. 外部变量（环境变量？），包括
        3.1 全局变量，包括DOM。  
        3.2 外部函数的变量或函数。
    
    如果一个函数访问了它的外部变量，那么它就是一个闭包。

    注意，外部函数不是必需的。
    通过访问外部变量，一个闭包可以维持（keep alive）这些变量。
    在内部函数和外部函数的例子中，外部函数可以创建局部变量，并且最终退出；
    但是，如果任何一个或多个内部函数在它退出后却没有退出，那么内部函数就维持了外部函数的局部数据。

    一个典型的例子就是全局变量的使用。

    从技术上来讲，在JS中，每个function都是闭包，因为它总是能访问在它外部定义的数据
 */

 // 闭包经常用于创建含有隐藏数据的函数（但并不总是这样）
var db = (function () {
    // 创建一个隐藏的 object，这个 object 持有一些数据
    // 从外部是不能访问这个 object 的
    var data = {}
    // 创建一个函数，这个函数提供一些访问 data 的数据的方法
    return function (key, val) {
        if (val === undefined) {
            return data[key] // get
        } else {
            return data[key] = val // set
        }
    }
    // 可以调用这个匿名方法
    // 返回这个内部函数，它是一个闭包
})()

db('x') // undefined
db('x', 1) // 设置 data['x'] = 1
db('x') // 1
// 我们不可能访问 data 这个 object 本身
// 但是我们可以设置它的成员